﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace DotNetCommon.Extensions;

/// <summary>
/// <see cref="IList{T}"/> 和 <see cref="List{T}"/> 的扩展方法
/// </summary>
public static class ListExtensions
{
    #region 系统方法补充 IList<T>.Add/AddRange/Remove
    /// <summary>
    /// 主要用来做List的初始化: <code>new List&lt;int>{1,new int[]{2,3},4}</code>
    /// </summary>
    /// <remarks>注意: 如果 list 或 items 为null, 则抛异常(和系统List行为保持一致)</remarks>
    /// <returns>返回自身</returns>
    public static IList<T> Add<T>(this IList<T> list, IEnumerable<T> items)
    {
        AssertUtil.NotNull(list);
        AssertUtil.NotNull(items);
        foreach (var item in items) list.Add(item);
        return list;
    }

    /// <summary>
    /// 将给定的<paramref name="items"/>添加到给定的<paramref name="list"/>。
    /// </summary>
    /// <remarks>如果 list 或 items 为null, 则抛异常(和系统List行为保持一致)</remarks>
    /// <returns>返回自身</returns>
    public static IList<T> AddRange<T>(this IList<T> list, IEnumerable<T> items)
    {
        AssertUtil.NotNull(list);
        AssertUtil.NotNull(items);
        foreach (var item in items) list.Add(item);
        return list;
    }

    /// <summary>
    /// 从指定的集合中移除符合条件的元素
    /// </summary>
    /// <remarks>如果 list 或 predicate 为null, 则抛异常(和系统List行为保持一致)</remarks>
    /// <returns>返回自身</returns>
    public static IList<T> Remove<T>(this IList<T> list, Func<T, bool> predicate)
    {
        AssertUtil.NotNull(list);
        AssertUtil.NotNull(predicate);
        for (var i = 0; i < list.Count; i++)
        {
            if (predicate(list[i]))
            {
                list.RemoveAt(i);
                i--;
            }
        }
        return list;
    }
    #endregion

    #region Fluent风格 List<T> IList<T> AddFluent AddIfNotExistFluent AddRangeFluent AddRangeIfNotExistFluent ClearFluent RemoveAtFluent InsertAtFluent
    /// <summary>
    /// Fluent风格: 向集合中添加新项,并返回自身
    /// </summary>
    /// <remarks>
    /// 注意:
    /// <list type="bullet">
    /// <item>如果集合为null,抛出异常</item>
    /// </list>
    /// </remarks>
    public static List<T> AddFluent<T>(this List<T> list, T item)
    {
        AssertUtil.NotNull(list);
        list.Add(item);
        return list;
    }

    /// <summary>
    /// Fluent风格: 如果集合中不存在,则向集合中添加新项,并返回自身
    /// </summary>
    /// <remarks>
    /// 注意:
    /// <list type="bullet">
    /// <item>如果集合为null,抛出异常</item>
    /// </list>
    /// </remarks>
    public static List<T> AddIfNotExistFluent<T>(this List<T> list, T item)
    {
        return AddIfNotExistFluent(list, item, EqualityComparer<T>.Default);
    }

    /// <inheritdoc cref="AddIfNotExistFluent{T}(List{T}, T)" />
    public static List<T> AddIfNotExistFluent<T>(this List<T> list, T item, IEqualityComparer<T> compare)
    {
        AssertUtil.NotNull(list);
        compare ??= EqualityComparer<T>.Default;
        var idx = list.FindIndex(t => compare.Equals(t, item));
        if (idx < 0) list.Add(item);
        return list;
    }

    /// <inheritdoc cref="AddFluent{T}(List{T}, T)" />/>
    public static IList<T> AddFluent<T>(this IList<T> list, T item)
    {
        AssertUtil.NotNull(list);
        list.Add(item);
        return list;
    }

    /// <inheritdoc cref="AddIfNotExistFluent{T}(List{T}, T)" />
    public static IList<T> AddIfNotExistFluent<T>(this IList<T> list, T item)
    {
        return AddIfNotExistFluent(list, item, EqualityComparer<T>.Default);
    }

    /// <inheritdoc cref="AddIfNotExistFluent{T}(List{T}, T)" />
    public static IList<T> AddIfNotExistFluent<T>(this IList<T> list, T item, IEqualityComparer<T> compare)
    {
        AssertUtil.NotNull(list);
        compare ??= EqualityComparer<T>.Default;
        for (var idx = 0; idx < list.Count; idx++)
        {
            if (compare.Equals(list[idx], item)) return list;
        }
        list.Add(item);
        return list;
    }

    /// <summary>
    /// Fluent风格: 向集合中添加新项集合,并返回自身
    /// </summary>
    /// <remarks>
    /// 注意:
    /// <list type="bullet">
    /// <item>如果 list 为null,抛出异常</item>
    /// <item>如果 items 为null,则忽略添加</item>
    /// </list>
    /// </remarks>
    public static List<T> AddRangeFluent<T>(this List<T> list, IEnumerable<T> items)
    {
        AssertUtil.NotNull(list);
        if (items == null) return list;
        list.AddRange(items);
        return list;
    }

    /// <inheritdoc cref="AddRangeIfNotExistFluent{T}(List{T}, IEnumerable{T}, IEqualityComparer{T}, bool)" />/>
    public static List<T> AddRangeIfNotExistFluent<T>(this List<T> list, IEnumerable<T> items)
    {
        return AddRangeIfNotExistFluent(list, items, EqualityComparer<T>.Default, false);
    }

    /// <inheritdoc cref="AddRangeIfNotExistFluent{T}(List{T}, IEnumerable{T}, IEqualityComparer{T}, bool)" />/>
    public static List<T> AddRangeIfNotExistFluent<T>(this List<T> list, IEnumerable<T> items, bool isDistinct)
        => AddRangeIfNotExistFluent(list, items, EqualityComparer<T>.Default, isDistinct);

    /// <inheritdoc cref="AddRangeIfNotExistFluent{T}(List{T}, IEnumerable{T}, IEqualityComparer{T}, bool)" />/>
    public static List<T> AddRangeIfNotExistFluent<T>(this List<T> list, IEnumerable<T> items, IEqualityComparer<T> compare)
        => AddRangeIfNotExistFluent(list, items, compare, false);

    /// <summary>
    /// Fluent风格: 如果集合中不存在,则向集合中添加新项,并返回自身
    /// </summary>
    /// <remarks>
    /// 注意:
    /// <list type="bullet">
    /// <item>如果集合为null,抛出异常</item>
    /// <item>如果传入 isDistinct:true, 则相当于最后对集合又做了一遍 Distinct</item>
    /// </list>
    /// </remarks>
    public static List<T> AddRangeIfNotExistFluent<T>(this List<T> list, IEnumerable<T> items, IEqualityComparer<T> compare, bool isDistinct)
    {
        AssertUtil.NotNull(list);
        compare ??= EqualityComparer<T>.Default;
        if (isDistinct)
        {
            list.AddRange(items ?? []);
            for (var i = 0; i < list.Count - 1; i++)
            {
                for (var j = i + 1; j < list.Count; j++)
                {
                    if (compare.Equals(list[j], list[i]))
                    {
                        list.RemoveAt(j);
                        j--;
                    }
                }
            }
            return list;
        }
        if (items == null) return list;
        foreach (var item in items)
        {
            var idx = list.FindIndex(t => compare.Equals(t, item));
            if (idx < 0) list.Add(item);
        }
        return list;
    }

    /// <inheritdoc cref="AddRangeFluent{T}(List{T}, IEnumerable{T})" />/>
    public static IList<T> AddRangeFluent<T>(this IList<T> list, IEnumerable<T> items)
    {
        AssertUtil.NotNull(list);
        if (items == null) return list;
        foreach (var item in items) list.Add(item);
        return list;
    }

    /// <inheritdoc cref="AddRangeIfNotExistFluent{T}(List{T}, IEnumerable{T}, IEqualityComparer{T}, bool)" />/>
    public static IList<T> AddRangeIfNotExistFluent<T>(this IList<T> list, IEnumerable<T> items)
    {
        return AddRangeIfNotExistFluent(list, items, EqualityComparer<T>.Default, false);
    }

    /// <inheritdoc cref="AddRangeIfNotExistFluent{T}(List{T}, IEnumerable{T}, IEqualityComparer{T}, bool)" />/>
    public static IList<T> AddRangeIfNotExistFluent<T>(this IList<T> list, IEnumerable<T> items, bool isDistinct)
        => AddRangeIfNotExistFluent(list, items, EqualityComparer<T>.Default, isDistinct);

    /// <inheritdoc cref="AddRangeIfNotExistFluent{T}(List{T}, IEnumerable{T}, IEqualityComparer{T}, bool)" />/>
    public static IList<T> AddRangeIfNotExistFluent<T>(this IList<T> list, IEnumerable<T> items, IEqualityComparer<T> compare)
        => AddRangeIfNotExistFluent(list, items, compare, false);

    /// <inheritdoc cref="AddRangeIfNotExistFluent{T}(List{T}, IEnumerable{T}, IEqualityComparer{T}, bool)" />
    public static IList<T> AddRangeIfNotExistFluent<T>(this IList<T> list, IEnumerable<T> items, IEqualityComparer<T> compare, bool isDistinct)
    {
        AssertUtil.NotNull(list);
        compare ??= EqualityComparer<T>.Default;
        if (isDistinct)
        {
            list.AddRange(items ?? []);
            for (var i = 0; i < list.Count - 1; i++)
            {
                for (var j = i + 1; j < list.Count; j++)
                {
                    if (compare.Equals(list[j], list[i]))
                    {
                        list.RemoveAt(j);
                        j--;
                    }
                }
            }
            return list;
        }
        if (items == null) return list;
        foreach (var item in items)
        {
            var hasFound = false;
            for (var i = 0; i < list.Count; i++)
            {
                if (compare.Equals(list[i], item))
                {
                    hasFound = true;
                    break;
                }
            }
            if (!hasFound) list.Add(item);
        }
        return list;
    }

    /// <summary>
    /// Fluent风格: 清空集合并返回自身
    /// </summary>
    /// <remarks>
    /// 注意:
    /// <list type="bullet">
    /// <item>如果集合为null,抛出异常</item>
    /// </list>
    /// </remarks>
    public static List<T> ClearFluent<T>(this List<T> list)
    {
        AssertUtil.NotNull(list);
        list.Clear();
        return list;
    }

    /// <inheritdoc cref="ClearFluent{T}(List{T})"/>/>
    public static IList<T> ClearFluent<T>(this IList<T> list)
    {
        AssertUtil.NotNull(list);
        list.Clear();
        return list;
    }

    /// <summary>
    /// Fluent风格: 从集合中移除某项
    /// </summary>
    /// <remarks>
    /// 注意:
    /// <list type="bullet">
    /// <item>如果集合为null,抛出异常</item>
    /// </list>
    /// </remarks>
    public static List<T> RemoveAtFluent<T>(this List<T> list, int index)
    {
        AssertUtil.NotNull(list);
        list.RemoveAt(index);
        return list;
    }

    /// <inheritdoc cref="RemoveAtFluent{T}(List{T}, int)"/>/>
    public static IList<T> RemoveAtFluent<T>(this IList<T> list, int index)
    {
        AssertUtil.NotNull(list);
        list.RemoveAt(index);
        return list;
    }

    /// <summary>
    /// Fluent风格: 向集合中插入某项
    /// </summary>
    /// <remarks>
    /// 注意:
    /// <list type="bullet">
    /// <item>如果集合为null,抛出异常</item>
    /// </list>
    /// </remarks>
    public static List<T> InsertAtFluent<T>(this List<T> list, int index, T b)
    {
        AssertUtil.NotNull(list);
        list.Insert(index, b);
        return list;
    }

    /// <inheritdoc cref="InsertAtFluent{T}(List{T}, int, T)"/>
    public static IList<T> InsertAtFluent<T>(this IList<T> list, int index, T b)
    {
        AssertUtil.NotNull(list);
        list.Insert(index, b);
        return list;
    }

    /// <inheritdoc cref="SetFluent{T}(List{T}, int, T)"/>
    public static IList<T> SetFluent<T>(this IList<T> list, int index, T b)
    {
        AssertUtil.NotNull(list);
        list[index] = b;
        return list;
    }

    /// <summary>
    /// Fluent风格: 设置集合中指定索引的值
    /// </summary>
    /// <remarks>
    /// 注意:
    /// <list type="bullet">
    /// <item>如果集合为null或索引超出,抛出异常</item>
    /// <item>如果func为null,则不做任何修改</item>
    /// </list>
    /// </remarks>
    public static IList<T> SetUpdateFluent<T>(this IList<T> list, int index, Func<T, T> func)
    {
        AssertUtil.NotNull(list);
        if (func == null) return list;
        list[index] = func(list[index]);
        return list;
    }

    /// <summary>
    /// Fluent风格: 设置集合中指定索引的值
    /// </summary>
    /// <remarks>
    /// 注意:
    /// <list type="bullet">
    /// <item>如果集合为null或索引超出,抛出异常</item>
    /// <item>如果filter为null,抛出异常</item>
    /// <item>如果func为null,则不做任何修改</item>
    /// </list>
    /// </remarks>
    public static IList<T> SetUpdateFluent<T>(this IList<T> list, Func<T, bool> filter, Func<T, T> func)
    {
        AssertUtil.NotNull(list);
        AssertUtil.NotNull(filter);
        if (func == null) return list;
        for (int i = 0; i < list.Count; i++)
        {
            var b = filter.Invoke(list[i]);
            if (b == false) continue;
            list[i] = func(list[i]);
        }
        return list;
    }

    /// <summary>
    /// Fluent风格: 设置集合中指定索引的值
    /// </summary>
    /// <remarks>
    /// 注意:
    /// <list type="bullet">
    /// <item>如果集合为null或索引超出,抛出异常</item>
    /// </list>
    /// </remarks>
    public static List<T> SetFluent<T>(this List<T> list, int index, T b)
    {
        AssertUtil.NotNull(list);
        list[index] = b;
        return list;
    }

    /// <summary>
    /// Fluent风格: 设置集合中指定索引的值
    /// </summary>
    /// <remarks>
    /// 注意:
    /// <list type="bullet">
    /// <item>如果集合为null或索引超出,抛出异常</item>
    /// <item>如果func为null,则不做任何修改</item>
    /// </list>
    /// </remarks>
    public static List<T> SetUpdateFluent<T>(this List<T> list, int index, Func<T, T> func)
    {
        AssertUtil.NotNull(list);
        if (func == null) return list;
        list[index] = func(list[index]);
        return list;
    }

    /// <summary>
    /// Fluent风格: 批量设置集合中的值
    /// </summary>
    /// <remarks>
    /// 注意:
    /// <list type="bullet">
    /// <item>如果集合为null,抛出异常</item>
    /// <item>如果filter为null,抛出异常</item>
    /// <item>如果func为null,则不做任何修改</item>
    /// </list>
    /// </remarks>
    public static List<T> SetUpdateFluent<T>(this List<T> list, Func<T, bool> filter, Func<T, T> func)
    {
        AssertUtil.NotNull(list);
        AssertUtil.NotNull(filter);
        if (func == null) return list;
        for (int i = 0; i < list.Count; i++)
        {
            var b = filter.Invoke(list[i]);
            if (b == false) continue;
            list[i] = func(list[i]);
        }
        return list;
    }

    /// <summary>
    /// Fluent风格: 从集合中移除某项(仅移除第一个)
    /// </summary>
    /// <remarks>
    /// 注意:
    /// <list type="bullet">
    /// <item>如果集合为null,抛出异常</item>
    /// </list>
    /// </remarks>
    public static List<T> RemoveFluent<T>(this List<T> list, T b)
    {
        AssertUtil.NotNull(list);
        var idx = list.IndexOf(b);
        if (idx >= 0) list.RemoveAt(idx);
        return list;
    }

    /// <inheritdoc cref="RemoveFluent{T}(List{T}, T)"/>
    public static IList<T> RemoveFluent<T>(this IList<T> list, T b)
    {
        AssertUtil.NotNull(list);
        var idx = list.IndexOf(b);
        if (idx >= 0) list.RemoveAt(idx);
        return list;
    }

    /// <summary>
    /// Fluent风格: 从集合中移除所有符合条件的项
    /// </summary>
    /// <remarks>
    /// 注意:
    /// <list type="bullet">
    /// <item>如果集合为null,抛出异常</item>
    /// <item>如果match为null,抛出异常</item>
    /// </list>
    /// </remarks>
    public static List<T> RemoveFluent<T>(this List<T> list, Predicate<T> match)
    {
        AssertUtil.NotNull(list);
        AssertUtil.NotNull(match);
        for (var i = 0; i < list.Count; i++)
        {
            if (match(list[i]))
            {
                list.RemoveAt(i);
                i--;
            }
        }
        return list;
    }
    /// <inheritdoc cref="RemoveFluent{T}(List{T}, Predicate{T})"/>
    public static IList<T> RemoveFluent<T>(this IList<T> list, Predicate<T> match)
    {
        AssertUtil.NotNull(list);
        AssertUtil.NotNull(match);
        for (var i = 0; i < list.Count; i++)
        {
            if (match(list[i]))
            {
                list.RemoveAt(i);
                i--;
            }
        }
        return list;
    }
    #endregion

    #region 循环操作 Next/Pre/ReverseFluent/ReSetStart
    /// <summary>
    /// 循环操作场景(一个多边形的点位遍历), 返回指定索引的下一个元素,如:
    /// <code>
    /// new List&lt;Vector2> { new(0, 0), new(1, 0), new(1, 1) }.Next(2).ShouldBe(new(0, 0));
    /// </code>
    /// </summary>
    public static T Next<T>(this IList<T> list, int index)
    {
        AssertUtil.NotNull(list);
        return list[(index + 1) % list.Count];
    }
    /// <summary>
    /// 循环操作场景(一个多边形的点位遍历),返回指定索引的上一个元素,如:
    /// <code>
    /// new List&lt;Vector2> { new(0, 0), new(1, 0), new(1, 1) }.Pre(0).ShouldBe(new(1, 1));
    /// </code>
    /// </summary>
    public static T Pre<T>(this IList<T> list, int index)
    {
        AssertUtil.NotNull(list);
        return list[(index - 1 + list.Count) % list.Count];
    }
    /// <summary>
    /// 将自身翻转, 并返回自身,如下:
    /// <code>
    /// new List&lt;int> { 1, 2, 3, 4 }.ReverseFluent().ShouldBe(new List&lt;int> { 4, 3, 2, 1 })
    /// </code>
    /// </summary>
    /// <remarks>
    /// 注意:
    /// <list type="bullet">
    /// <item>如果集合为null,抛出异常</item>
    /// </list>
    /// </remarks>
    public static List<T> ReverseFluent<T>(this List<T> list)
    {
        AssertUtil.NotNull(list);
        list.Reverse();
        return list;
    }
    /// <inheritdoc cref="ReverseFluent{T}(List{T})"/>
    public static IList<T> ReverseFluent<T>(this IList<T> list)
    {
        AssertUtil.NotNull(list);
        if (list.Count > 1)
        {
            var mid = list.Count / 2;
            //偶数: 0 1 2 3
            //Count=4, mid=2
            //奇数: 0 1 2 3 4
            //Count=5, mid=2
            for (int i = 0; i < mid; i++)
                (list[i], list[list.Count - 1 - i]) = (list[list.Count - 1 - i], list[i]);
        }
        return list;
    }
    /// <summary>
    /// 把集合想象成一个圆圈, 这里重新选择集合起始点和前进方向, 返回新的集合, 图形示意:
    /// <code>
    ///  0--------->1                                                1&lt;---------0
    /// /|\         |                                                |         /|\
    ///  |          |   list.ReSetStart(index:1, reverse:true)       |          |   
    ///  |          |  ---------------------------------------->     |          |  
    ///  |         \|/                                              \|/         |
    ///  3&lt;---------2                                                2--------->3
    ///  </code>
    ///  代码示例:
    ///  <code>
    /// new List&lt;int> { 1, 2, 3, 4 }.ReSetStart(2).ShouldBe(new List&lt;int> { 3, 4, 1, 2 });
    /// new List&lt;int> { 1, 2, 3, 4 }.ReSetStart(2, reverse:true).ShouldBe(new List&lt;int> { 3, 2, 1, 4 });
    /// new List&lt;int> { 1, 2, 3, 4 }.ReSetStart(-1).ShouldBe(new List&lt;int> { 4, 1, 2, 3 });
    /// </code>
    /// </summary>
    /// <remarks>
    /// 注意:
    /// <list type="bullet">
    /// <item>如果集合为null,抛出异常</item>
    /// </list>
    /// </remarks>
    public static List<T> ReSetStart<T>(this List<T> list, int index, bool reverse = false)
    {
        AssertUtil.NotNull(list);
        index %= list.Count;
        if (index < 0) index += list.Count;
        if (reverse)
        {
            return list.Take(index + 1).Reverse().Concat(list.Skip(index + 1).Take(list.Count - index - 1).Reverse()).ToList();
        }
        else
        {
            return list.Skip(index).Take(list.Count - index).Concat(list.Take(index)).ToList();
        }
    }

    /// <inheritdoc cref="ReSetStart{T}(List{T}, int, bool)"/>
    public static IList<T> ReSetStart<T>(this IList<T> list, int index, bool reverse = false)
    {
        AssertUtil.NotNull(list);
        index %= list.Count;
        if (index < 0) index += list.Count;
        if (reverse)
        {
            return list.Take(index + 1).Reverse().Concat(list.Skip(index + 1).Take(list.Count - index - 1).Reverse()).ToList();
        }
        else
        {
            return list.Skip(index).Take(list.Count - index).Concat(list.Take(index)).ToList();
        }
    }
    #endregion

    #region IList.FindIndex/FindLastIndex
    /// <summary>
    /// 类似 list.FindIndex
    /// </summary>
    /// <remarks>
    /// 注意:
    /// <list type="bullet">
    /// <item>如果集合为null,抛出异常</item>
    /// <item>如果match为null,抛出异常</item>
    /// </list>
    /// </remarks>
    public static int FindIndex<T>(this IList<T> list, Predicate<T> match)
    {
        AssertUtil.NotNull(list);
        AssertUtil.NotNull(match);
        for (int i = 0; i < list.Count; i++) if (match(list[i])) return i;
        return -1;
    }
    /// <summary>
    /// 类似 list.FindLastIndex
    /// </summary>
    /// <remarks>
    /// 注意:
    /// <list type="bullet">
    /// <item>如果集合为null,抛出异常</item>
    /// <item>如果match为null,抛出异常</item>
    /// </list>
    /// </remarks>
    public static int FindLastIndex<T>(this IList<T> list, Predicate<T> match)
    {
        AssertUtil.NotNull(list);
        AssertUtil.NotNull(match);
        for (int i = list.Count - 1; i >= 0; i--) if (match(list[i])) return i;
        return -1;
    }
    #endregion
}